package qe.spider.vo;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.NumberChineseFormatter;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import qe.spider.MainSelect;
import qe.spider.grab.ParseThsStock;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static cn.hutool.core.date.DatePattern.NORM_TIME_FORMATTER;

@Slf4j
@Data
public class StockList {
    @SerializedName(value = "Total", alternate = "Count")
    private BigDecimal Total;
    private String Date;
    private List<String> Day;
    private BigDecimal Time;
    private BigDecimal ttag;
    private String errcode;

    @SerializedName(value = "List", alternate = "list")
    private List<Stock> stockList = new ArrayList<>();

    @Expose
    private Date grabTime = new Date();

    public void setTime(BigDecimal time) {
        Time = time;
        grabTime = new Date(time.longValue() * 1000);
    }

    public Date grabTime() {
        Date t = grabTime == null ? grabTime = new Date(Time.longValue() * 1000) : grabTime;
        stockList.forEach(stock -> {
            if (stock.getGrabTime() == null) stock.setGrabTime(t);
            stock.setZhangfu(stock.getZhangfu());
        });
        return t;
    }

    @Expose
    private transient ParseThsStock parser;

    public List<File> latestTHFFile(String day) {
        Comparator<File> fileTimeDescComparator = (f1, f2) -> {
            Date d1 = f1.getName().contains("_") ? DateUtil.parse(day + " " + f1.getName().split("\\.")[0], "yyyy-MM-dd HHmmss_SSS") : DateUtil.parse(day + " " + f1.getName().split("\\.")[0], "yyyy-MM-dd HHmmss");
            Date d2 = f2.getName().contains("_") ? DateUtil.parse(day + " " + f2.getName().split("\\.")[0], "yyyy-MM-dd HHmmss_SSS") : DateUtil.parse(day + " " + f2.getName().split("\\.")[0], "yyyy-MM-dd HHmmss");
            return d2.compareTo(d1);
        };
        List<File> allFile = ParseThsStock.disks.stream().map(path ->
                        FileUtil.loopFiles(path + "\\" + day, File::isFile)
                ).flatMap(Collection::stream)
                .filter(f -> f.getName().endsWith(".xls") && f.length() > 1000000)
                .filter(f -> (f.getName().contains("_") ? DateUtil.parse(day + " " + f.getName().split("\\.")[0], "yyyy-MM-dd HHmmss_SSS") : DateUtil.parse(day + " " + f.getName().split("\\.")[0], "yyyy-MM-dd HHmmss")).isBefore(grabTime))
                .sorted(fileTimeDescComparator)
                .toList();
        return allFile;
    }

    //将同花顺autoit的数据merge进来
    public List<Stock> mergeThs() throws IOException {
        String day = DateUtil.format(grabTime, "yyyy-MM-dd");
        List<File> allFile = latestTHFFile(day);
        if (allFile.isEmpty()) {
            log.error("未抓取到同花顺个股数据");
            return new ArrayList<>();
        }
        File latestFile = allFile.get(0);
        long diff = DateUtil.between(grabTime, latestFile.getName().contains("_") ? DateUtil.parse(day + " " + latestFile.getName().split("\\.")[0], "yyyy-MM-dd HHmmss_SSS") : DateUtil.parse(day + " " + latestFile.getName().split("\\.")[0], "yyyy-MM-dd HHmmss"), DateUnit.MINUTE);
        log.info("抓取到的同花顺个股数据文件为：{}，据抓取时间{}有{}分钟了（盘中不能超过1分钟）", latestFile.getAbsolutePath(), DateUtil.format(grabTime, "HHmmss"), diff);
        if (/*DateUtil.now().substring(11, 16).compareTo("09:15") >= 0 && DateUtil.now().substring(11, 16).compareTo("15:00") <= 0 && */diff > 1) {
            log.error("盘中同花顺最新的行情数据超过1分钟，过期了----最新的文件为：{}", latestFile.getAbsolutePath());
            return new ArrayList<>();
        }
        parser = new ParseThsStock(latestFile);
        parser.parseAllLines();
        if (stockList.isEmpty()) return stockList = parser.getAll();
        for (Stock s : stockList) {
            if (parser.getStocks().containsKey(s.getStock_code())) {
                Stock ths = parser.getStocks().get(s.getStock_code());
                s.merge(ths);
            }
        }
        return stockList;
    }

    public List<Stock> top() {
        List<Stock> jingETop = stockList.stream().filter(s -> s.getZiJinJingE() != null).sorted(Comparator.comparing(Stock::getZiJinJingE).reversed()).limit(MainSelect.factor.getZiJinJingETopN()).toList();

        List<Stock> jingLiangTop = stockList.stream().filter(s -> s.getZhuLiJingLiang() != null).sorted(Comparator.comparing(Stock::getZhuLiJingLiang).reversed()).limit(MainSelect.factor.getJingLiangTopN()).toList();

        List<Stock> cross = new ArrayList<>(CollectionUtil.intersectionDistinct(jingETop, jingLiangTop));
        if (log.isDebugEnabled())
            log.debug("净额前{} : {}", MainSelect.factor.getZiJinJingETopN(), jingETop.stream().map(s -> s.codeName() + " 净额（" + NumberChineseFormatter.formatSimple(s.getZiJinJingE().longValue()) + "）:" + s.getZiJinJingE()).collect(Collectors.joining(", ")));
        if (log.isDebugEnabled())
            log.debug("净量前{} : {}", MainSelect.factor.getJingLiangTopN(), jingLiangTop.stream().map(s -> s.codeName() + " 净量:" + s.getZhuLiJingLiang()).collect(Collectors.joining(", ")));
        log.info("个股净额前{}与净量前{}交集 : \r\n{}\r\n", MainSelect.factor.getZiJinJingETopN(), MainSelect.factor.getJingLiangTopN(),
                cross.stream().map(s -> DateUtil.format(s.getGrabTime(), NORM_TIME_FORMATTER) + "\t" + s.codeName() + "\t涨幅:" + s.getZhangfu() + ",净额:" + (s.getZiJinJingE() == null ? "" : NumberChineseFormatter.formatSimple(s.getZiJinJingE().longValue())) + ",净量:" + (s.getZhuLiJingLiang() == null ? "" : NumberUtil.decimalFormat("#.##%", s.getZhuLiJingLiang())) + ",主买:" + (s.getZhuBuyZhanBi() == null ? "" : NumberUtil.decimalFormat("#.##%", s.getZhuBuyZhanBi())) + ",主卖:" + (s.getZhuSellZhanBi() == null ? "" : NumberUtil.decimalFormat("#.##%", s.getZhuSellZhanBi())) + ",流通:" + (s.getShiJiLiuTongZhi() == null ? "" : NumberChineseFormatter.formatSimple(s.getShiJiLiuTongZhi().longValue())) + ",换手:" + s.getShiJiHuanShouLv() + ",价格:" + s.getJiage() + ",量比:" + s.getLiangBi() + ",涨速:" + s.getZhangSu4min() + "%,排名:" + s.getRenqiRanking() + ",利好:" + s.getLiHao() + ",利空:" + s.getLiKong() + ",涨次:" + s.getZhangTingCnt5() + ",跌次:" + s.getDieTingCnt5() + ",炸板:" + s.isZhaBan()).collect(Collectors.joining(",\r\n")));
        List<Stock> ZhuLiJingLiang = cross.stream().filter(s -> s.getZhuLiJingLiang() != null && NumberUtil.isGreaterOrEqual(NumberUtil.toBigDecimal(s.getZhuLiJingLiang()), NumberUtil.toBigDecimal(MainSelect.factor.getZhuLiJingLiang()))).toList();
        log.info("主力净量 >= {}: {}", MainSelect.factor.getZhuLiJingLiang(), ZhuLiJingLiang.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> ZhuBuyZhanBi = ZhuLiJingLiang.stream().filter(s -> s.getZhuBuyZhanBi() != null && NumberUtil.isGreaterOrEqual(NumberUtil.toBigDecimal(s.getZhuBuyZhanBi()), NumberUtil.toBigDecimal(MainSelect.factor.getZhuBuyZhanBi()))).toList();
        log.info("主力买入成交占比 >= {}: {}", MainSelect.factor.getZhuBuyZhanBi(), ZhuBuyZhanBi.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> ZhuSellZhanBi = ZhuBuyZhanBi.stream().filter(s -> s.getZhuSellZhanBi() != null && NumberUtil.isLessOrEqual(NumberUtil.toBigDecimal(s.getZhuSellZhanBi()), NumberUtil.toBigDecimal(MainSelect.factor.getZhuSellZhanBi()))).toList();
        log.info("主力卖出成交占比 <= {}: {}", MainSelect.factor.getZhuSellZhanBi(), ZhuSellZhanBi.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> ZiJinJingE = ZhuSellZhanBi.stream().filter(s -> s.getZiJinJingE() != null && NumberUtil.isGreaterOrEqual(NumberUtil.toBigDecimal(s.getZiJinJingE()), NumberUtil.toBigDecimal(MainSelect.factor.getZhuLiJingE() * 100000000))).toList();
        log.info("主力净额（亿） >= {}: {}", String.format("%.2f", MainSelect.factor.getZhuLiJingE()), ZiJinJingE.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> ShiJiLiuTongZhi = ZiJinJingE.stream().filter(s -> s.getShiJiLiuTongZhi() != null && NumberUtil.isIn(NumberUtil.toBigDecimal(s.getShiJiLiuTongZhi()), NumberUtil.toBigDecimal(MainSelect.factor.getShiJiLiuTongZhi() * 100000000), NumberUtil.toBigDecimal(MainSelect.factor.getShiJiLiuTongZhiUpper() * 100000000))).toList();
        log.info("{}亿 <= 实际流通 <= {}亿: {}", String.format("%.2f", MainSelect.factor.getShiJiLiuTongZhi()), String.format("%.2f", MainSelect.factor.getShiJiLiuTongZhiUpper()), ShiJiLiuTongZhi.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> ShiJiHuanShouLv = ShiJiLiuTongZhi.stream().filter(s -> s.getShiJiHuanShouLv() != null && NumberUtil.isGreaterOrEqual(NumberUtil.toBigDecimal(s.getShiJiHuanShouLv()), NumberUtil.toBigDecimal(MainSelect.factor.getShiJiHuanShouLv()))).toList();
        log.info("换手率 >= {}: {}", MainSelect.factor.getShiJiHuanShouLv(), ShiJiHuanShouLv.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> filter = ShiJiHuanShouLv.stream().filter(s -> s.getZiJinJingE() != null).sorted(Comparator.comparing(Stock::getZiJinJingE).reversed()).limit(MainSelect.factor.getZiJinJingETopN()).toList();
        log.info("资金净额排序后：{}", filter.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> price = filter.stream().filter(s -> s.getJiage() != null && NumberUtil.isIn(NumberUtil.toBigDecimal(s.getJiage()), NumberUtil.toBigDecimal(MainSelect.factor.getPriceLower()), NumberUtil.toBigDecimal(MainSelect.factor.getPriceUpper()))).toList();
        log.info("{} <= 股价 <= {} : {}", String.format("%.2f", MainSelect.factor.getPriceLower()), String.format("%.2f", MainSelect.factor.getPriceUpper()), price.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> exclude = MainSelect.factor.getExcludeZhangDieTingLatest() ? price.stream().filter(Stock::existZhangDieTing5Day).toList() : price;
        log.info("是否排除5日涨跌停的个股={}，排除涨跌停后：{}", MainSelect.factor.getExcludeZhangDieTingLatest(), exclude.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

//        if (null == parser || parser.getStocks().isEmpty()) return exclude;
        //同花顺抓到了个股数据，下面因子有效

//        log.info("现手和大笔买入叠加前----{}", exclude.stream().map(s -> s.codeName() + " 现手涨:" + s.getXianShouZhang() + " 现手:" + s.getXianShou()).collect(Collectors.joining(", ")));
        List<Stock> xianshou = MainSelect.factor.getXianShouZhang() ?
                exclude.stream().filter(s -> null != s.getXianShouZhang() && s.getXianShouZhang() && NumberUtil.isGreaterOrEqual(NumberUtil.toBigDecimal(s.getXianShou()), NumberUtil.toBigDecimal(MainSelect.factor.getXianShou()))).toList()
                : exclude.stream().filter(s -> NumberUtil.isGreaterOrEqual(NumberUtil.toBigDecimal(s.getXianShou()), NumberUtil.toBigDecimal(MainSelect.factor.getXianShou()))).toList();
        log.info("现手涨={}，且大笔买入 >= {}: {}", MainSelect.factor.getXianShouZhang(), MainSelect.factor.getXianShou(), xianshou.stream().map(Stock::codeName).collect(Collectors.joining(",")));

        List<Stock> liangBi = xianshou.stream().filter(s -> s.getLiangBi() != null && NumberUtil.isGreaterOrEqual(NumberUtil.toBigDecimal(s.getLiangBi()), NumberUtil.toBigDecimal(MainSelect.factor.getLiangBi()))).toList();
        log.info("量比 >= {}: {}", MainSelect.factor.getLiangBi(), liangBi.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> zhangSu4min = liangBi.stream().filter(s -> s.getZhangSu4min() != null && NumberUtil.isGreaterOrEqual(NumberUtil.toBigDecimal(s.getZhangSu4min()), NumberUtil.toBigDecimal(MainSelect.factor.getZhangSu4min()))).toList();
        log.info("4分钟涨速 >= {}: {}", MainSelect.factor.getZhangSu4min(), zhangSu4min.stream().map(Stock::codeName).collect(Collectors.joining(", ")));


//        List<Stock> renQiTop = new ArrayList<>(CollectionUtil.intersectionDistinct(renQiTopForAll, zhangSu4min));
        List<Stock> renQiTop = zhangSu4min.stream().filter(s -> s.getRenqiRanking() != null && s.getRenqiRanking() <= MainSelect.factor.getRenQiTop()).toList();
        log.info("市场人气排名前 <= {}: {}", MainSelect.factor.getRenQiTop(), renQiTop.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> lihao = MainSelect.factor.getLiHao() ? renQiTop.stream().filter(Stock::getLiHao).toList() : renQiTop;
        log.info("必须有利好 = {}: {}", MainSelect.factor.getLiHao(), lihao.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> likong = MainSelect.factor.getLiKong() ? lihao.stream().filter(s -> !s.getLiKong()).toList() : lihao;
        log.info("必须无利空 = {}: {}", MainSelect.factor.getLiKong(), likong.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> zhangTingCnt = likong.stream().filter(s -> null != s.getZhangTingCnt5() && s.getZhangTingCnt5() <= MainSelect.factor.getZhangTingCnt()).toList();
        log.info("涨停次数 <= {}: {}", MainSelect.factor.getZhangTingCnt(), zhangTingCnt.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> dieTingCnt = zhangTingCnt.stream().filter(s -> null != s.getDieTingCnt5() && s.getDieTingCnt5() <= MainSelect.factor.getDieTingCnt()).toList();
        log.info("跌停次数 <= {}: {}", MainSelect.factor.getDieTingCnt(), dieTingCnt.stream().map(Stock::codeName).collect(Collectors.joining(",")));

        List<Stock> zhaBan = MainSelect.factor.getExcludeZhaBan() ? zhangTingCnt.stream().filter(s -> !s.isZhaBan()).toList() : dieTingCnt;
        log.info("排除炸板 = {}: {}", MainSelect.factor.getExcludeZhaBan(), zhaBan.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> zhangTing = MainSelect.factor.getUptoZhangTing() ? zhaBan.stream().filter(Stock::isUpLimit).toList() : zhaBan;
        log.info("当前价是否必须达到涨停价 = {}: {}", MainSelect.factor.getUptoZhangTing(), zhangTing.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> notZhangTing = MainSelect.factor.getExcludeAlreadyUpLimit() ? zhangTing.stream().filter(s -> !s.isUpLimit()).toList() : zhangTing;
        log.info("排除已涨停的个股 = {}: {}", MainSelect.factor.getExcludeAlreadyUpLimit(), notZhangTing.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> zhangFu = notZhangTing.stream().filter(s -> s.numZhangFu() >= MainSelect.factor.getUpZhangFu()).toList();
        log.info("上榜涨幅 >= {}: {}", MainSelect.factor.getUpZhangFu(), zhangFu.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        List<Stock> pool = MainSelect.factor.getPoolValidDays() >= 0 ? zhangFu.stream().filter(s -> StockPool.stockInPool(DateUtil.today(), s)).toList() : zhangFu;
        log.info("股票池的最大有效天数 = {}，股票池中的股票数量 = {} ： {}", MainSelect.factor.getPoolValidDays(), StockPool.getPoolStocks(DateUtil.today()).size(), pool.stream().map(Stock::codeName).collect(Collectors.joining(", ")));

        return pool;
    }

/*    private static Map<String, List<Stock>> zhangDieTingGrabs = new HashMap<>();

    public List<Stock> filterZhangDieTing(List<Stock> stocks) {
        if (!MainSelect.factor.getExcludeZhangDieTingLatest()) return stocks;
        if (zhangDieTingGrabs.containsKey(DateUtil.today())) {  //如果已有当日涨跌停数据，不必再抓
            Set<String> s1 = stocks.stream().map(Stock::getStock_code).collect(Collectors.toSet());
            Set<String> s2 = zhangDieTingGrabs.get(DateUtil.today()).stream().map(Stock::getStock_code).collect(Collectors.toSet());
            Set<String> s3 = stocks.stream().map(Stock::getStock_code).collect(Collectors.toSet());
            Set<String> s4 = zhangDieTingGrabs.get(DateUtil.today()).stream().map(Stock::getStock_code).collect(Collectors.toSet());
            if (s2.containsAll(s1) || s4.containsAll(s3)) {
                log.debug("{}已存在{}，不必再抓", s4, s3);
                return excludeZhangDieTing(stocks, zhangDieTingGrabs.get(DateUtil.today()));
            }
        }

//        String json = FileUtil.readString("WenCai.json", "UTF-8");
//        String token = JsonParser.parseString(json).getAsJsonObject().getAsJsonPrimitive("token").getAsString();
//        GrabWenCai_ZhangDieTing wencai = new GrabWenCai_ZhangDieTing(token);
        try {
            List<Stock> list = new ArrayList<>();
//            String res = wencai.grab(wencai.question(stocks));
//            List<Stock> zhangDieTing = wencai.parseAnswer(wencai.parseAnswer(res));

            if (null == GrabGubit.cookies || GrabGubit.cookies.isEmpty()) new GrabGubit_Login().grab();
//            new GrabGubit_ClearZiXuan().grab();

//            new GrabGubit_AddZiXuan().grab(stocks.stream().map(Stock::getStock_code).toList());
//            List<Stock> zhangDieTing = new GrabGubit_ZhangDieTing().grab().parseIsZhangDieTing();
            List<Stock> zhangDieTing = new GrabGubit_ZhangDieTing(stocks.stream().map(Stock::getStock_code).toList()).grab();
            log.info("其中5日内涨跌停的股票：{}", zhangDieTing.stream().filter(Stock::existZhangDieTing5Day).map(Stock::zhangdieTingInfo).collect(Collectors.joining(",")));
            zhangDieTingGrabs.put(DateUtil.today(), zhangDieTing);
//            log.info("涨跌停名单：{}", zhangDieTing.stream().map(s -> s.getStock_code() + "(" + s.getStock_name() + ")").collect(Collectors.joining(",")));

            return excludeZhangDieTing(stocks, zhangDieTing);
        } catch (Exception e) {
            log.error("获取'{}'涨跌停时错误:" + e.getMessage(), stocks.stream().map(Stock::getStock_code).collect(Collectors.joining(",")), e);
//            throw new RuntimeException("问财访问令牌过期或被封了，请更新token后再试！若无效，请检查是否数据格式变了");
        }
        return stocks;
    }


    private List<Stock> excludeZhangDieTing(List<Stock> stocks, List<Stock> zhangDieTing) {
        List<Stock> list = new ArrayList<>();
        stocks.forEach(s -> {   //不在涨跌停名单里
            Long cnt = zhangDieTing.stream().filter(sc -> {
                return sc.getStock_code().equals(s.getStock_code()) && sc.existZhangDieTing5Day();
            }).count();
//                if (zhangDieTing.stream().map(Stock::getStock_code).toList().contains(s.getStock_code())
//                        || zhangDieTing.stream().map(Stock::getStock_name).toList().contains(s.getStock_name())) {
            if (cnt > 0) {
                log.debug("{}（{}） 在涨跌停名单里", s.getStock_name(), s.getStock_code());
            } else {
                list.add(s);
            }
        });
        return list;
    }*/

}
